<?php

CRequire('CObject', 'CError', 'CType');

define('kSKRecordUntied', 'kSKRecordUntied');

global $_DKRecordSchemas;
$_DKRecordSchemas = array();

/*
Example of use:

class Employee extends DKRecord {
	var $_Attributes = array(
		'EmployeeID' => kCTypeInteger,
		'DeptID' => kCTypeInteger,
		'Name' => kCTypeString,
		'Surname' => kCTypeString,
		'DateOfEmployment' => kCTypeDate, // ...
		
		kDKIdentifiers => array('EmployeeID', 'DeptID');
	);
}

$s = &SKDatabase::Table('employees');
$emp = &new Employee($s,
	12345, // EmployeeID
	123 // DeptID -- in the order found in kDKIdentifiers.
	);

var_dump($emp->Name); // and so on
$emp->Name = 'John'; // and so on

$emp->Save();

*/

define ('kDKIncludeIdentifiers', null);
define ('kDKRemoveIdentifiers', 1);
define ('kDKOnlyIdentifiers', 2);

class DKRecord extends CObject {
	function Init(/* ... */) {
		$f = func_get_args();
		$s = &$f[0];
		if (is_object($s))
			array_splice($f, 0, 1);
		if (is_array($f[0])) {
			foreach ($f[0] as $name => $value)
				$this->SetAttribute($name, $value);
			array_splice($f, 0, 1);
		}
		
		$i = 0;
		foreach ($this->AttributesSchema() as $name => $type) {
			if (!isset($f[$i]))
				break;
			
			$this->SetAttribute($name, $f[$i]);
			$i++;
		}
		
		if (is_object($s))
			$this->LoadFrom($s);
	}

	function Attributes($ids = null) {
		$a = array();
		
		foreach ($this->AttributesSchema($ids) as $name => $type) {
			$a[$name] = &$this->Attribute($name);
		}
		
		return $a;
	}
	
	function AttributeType($name) {
		return @$this->Schema(strtolower($name));
	}
	
	function &Attribute($name) {
		foreach (get_object_vars($this) as $vname => $value) {
			if (strtolower($name) == strtolower($vname))
				return $this->$vname;
		}
		
		$x = null;
		return $x;
	}
	
	function SetAttributeRef($name, &$value) {		
		$schema = &CType::OfName($n = $this->AttributeType($name));
		
		if (CErr($e = $schema->Coerce($value)))
			return $e;
		$this->$name = &$value;
		return null;
	}
	
	function SetAttribute($name, $value) {
		return $this->SetAttributeRef($name, $value);
	}
	
	function IdentifierNames() {
		$x = @$this->Schema(kDKIdentifiers);
		if (is_null($x))
			$x = array();
		return $x;
	}

	function IdentifiersSchema() {
		$x = @$this->Schema(kDKIdentifiers);
		if (is_null($x))
			$ids = array();
		else {
			foreach ($x as $name)
				$ids[$name] = $this->Schema($name);
		}
		
		return $ids;
	}
	
	function AttributesSchema($identifier = null) {
		$a = array();
		foreach ($this->Schema() as $name => $type) {
			
			if (strlen($name) == 0 || $name[0] == '_' || $name[0] == '*')
				continue;
			
			if ($identifier == kDKRemoveIdentifiers && $this->IsIdentifier($name))
				continue;
			else if ($identifier == kDKOnlyIdentifiers && !$this->IsIdentifier($name))
				continue;
			
			
			$a[$name] = $type;
		}
		
		return $a;
	}

	function IsIdentifier($name) {
		return in_array($name, $this->Schema(kDKIdentifiers));
	}
	
	function IsAttribute($name) {
		return is_string($name) && isset($this->_Attributes[$name]);
	}
	
	var $_Storage;
	function SetStorage(&$s) {
		$this->_Storage = &$s;
	}
	
	function &Storage() {
		return $this->_Storage;
	}
	
	function Save() {
		$s = &$this->Storage();
		if (is_null($s))
			return new CError(kSKRecordUntied, "This record was not saved in a storage previously.", null);
			
		return $s->Save($this);
	}
	
	function SaveTo(&$s) {
		$olds = &$this->Storage();
		$this->SetStorage($s);
		$x = $this->Save();
		if (CErr($x))
			$this->SetStorage($olds);
		return $x;
	}
	
	function Load() {
		$s = &$this->Storage();
		if (is_null($s))
			return new CError(kSKRecordUntied, "This record was not saved in a storage previously.", null);
			
		return $s->LoadAttributes($this);
	}
	
	function LoadFrom(&$s) {
		$olds = &$this->Storage();
		$this->SetStorage($s);
		$x = $this->Reload();
		if (CErr($x))
			$this->SetStorage($olds);
		return $x;
	}
	
	function Delete() {
		$s = &$this->Storage();
		if (is_null($s))
			return new CError(kSKRecordUntied, "This record was not saved in a storage previously.", null);
			
		$result = $s->Delete($this);
		if (!CErr($result)) {
			$null = null;
			$this->SetStorage($null);
		}
		
		return $result;
	}
	
	function DeleteFrom($s) {
		$olds = &$this->Storage();
		$this->SetStorage($s);
		$x = $this->Save();
		if (CErr($x))
			$this->SetStorage($olds);
		return $x;
	}
	
	function &Schema($key = null) {
		if (is_null($key))
			return $GLOBALS['_DKRecordSchemas'][CSymbol(get_class($this))];
		else if ($key[0] != '*')
			return $GLOBALS['_DKRecordSchemas'][CSymbol(get_class($this))][strtolower($key)];
		else
			return $GLOBALS['_DKRecordSchemas'][CSymbol(get_class($this))][$key];
	}
	
	function SetSchemaForClass($class, $schema) {		
		global $_DKRecordSchemas;
		$newSchema = array();
		
		foreach ($schema as $name => $type) {
			
			if ($name[0] == '*')
				$newSchema[$name] = $type;
			else
				$newSchema[strtolower($name)] = $type;
			
		}
		
		$_DKRecordSchemas[CSymbol($class)] = $newSchema;
	}
}

?>